/*
 *
 *  * Copyright 2010-2016 OrientDB LTD (http://orientdb.com)
 *  *
 *  * Licensed under the Apache License, Version 2.0 (the "License");
 *  * you may not use this file except in compliance with the License.
 *  * You may obtain a copy of the License at
 *  *
 *  *      http://www.apache.org/licenses/LICENSE-2.0
 *  *
 *  * Unless required by applicable law or agreed to in writing, software
 *  * distributed under the License is distributed on an "AS IS" BASIS,
 *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  * See the License for the specific language governing permissions and
 *  * limitations under the License.
 *
 */

package com.orientechnologies.lucene.test;

import com.orientechnologies.orient.core.db.ODatabaseDocumentInternal;
import com.orientechnologies.orient.core.db.OrientDB;
import com.orientechnologies.orient.core.db.OrientDBConfig;
import com.orientechnologies.orient.core.db.document.ODatabaseDocument;
import com.orientechnologies.orient.core.id.ORID;
import com.orientechnologies.orient.core.id.ORecordId;
import com.orientechnologies.orient.core.index.OIndex;
import com.orientechnologies.orient.core.metadata.schema.OClass;
import com.orientechnologies.orient.core.metadata.schema.OType;
import com.orientechnologies.orient.core.record.OElement;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.sql.executor.OResult;
import com.orientechnologies.orient.core.sql.executor.OResultSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.Assert;
import org.junit.Test;

/** Created by Enrico Risa on 10/08/15. */
public class LuceneTransactionEmbeddedQueryTest {

  public LuceneTransactionEmbeddedQueryTest() {}

  @Test
  public void testRollback() {

    try (OrientDB orientdb = new OrientDB("memory", OrientDBConfig.defaultConfig())) {
      orientdb.execute(
          "create database updateTxTest memory users(admin identified by 'adminpwd' role"
              + " admin)");

      try (ODatabaseDocumentInternal db =
          (ODatabaseDocumentInternal) orientdb.open("updateTxTest", "admin", "adminpwd")) {
        createSchema(db);
        ODocument doc = new ODocument("c1");
        doc.field("p1", new String[] {"abc"});
        db.begin();
        db.save(doc);

        String query = "select from C1 where p1 lucene \"abc\" ";
        OResultSet vertices = db.query(query);

        Assert.assertEquals(vertices.stream().count(), 1);
        db.rollback();

        query = "select from C1 where p1 lucene \"abc\" ";
        vertices = db.query(query);
        Assert.assertEquals(vertices.stream().count(), 0);
      }
      orientdb.drop("updateTxTest");
    }
  }

  private static void createSchema(ODatabaseDocument db) {
    final OClass c1 = db.createVertexClass("C1");
    c1.createProperty("p1", OType.EMBEDDEDLIST, OType.STRING);
    c1.createIndex("C1.p1", "FULLTEXT", null, null, "LUCENE", new String[] {"p1"});
  }

  @Test
  public void txRemoveTest() {

    try (OrientDB orientdb = new OrientDB("memory", OrientDBConfig.defaultConfig())) {
      orientdb.execute(
          "create database updateTxTest memory users(admin identified by 'adminpwd' role"
              + " admin)");

      try (ODatabaseDocumentInternal db =
          (ODatabaseDocumentInternal) orientdb.open("updateTxTest", "admin", "adminpwd")) {
        createSchema(db);
        db.begin();

        ODocument doc = new ODocument("c1");
        doc.field("p1", new String[] {"abc"});

        OIndex index = db.getMetadata().getIndexManagerInternal().getIndex(db, "C1.p1");

        db.save(doc);

        String query = "select from C1 where p1 lucene \"abc\" ";
        OResultSet vertices = db.query(query);

        Assert.assertEquals(1, vertices.stream().count());

        Assert.assertEquals(1, index.getInternal().size());
        db.commit();

        query = "select from C1 where p1 lucene \"abc\" ";
        vertices = db.query(query);

        OResult res = vertices.next();
        Assert.assertEquals(1, index.getInternal().size());

        db.begin();

        db.delete(res.getIdentity().get());

        query = "select from C1 where p1 lucene \"abc\" ";
        vertices = db.query(query);

        Collection coll;
        try (Stream<ORID> stream = index.getInternal().getRids("abc")) {
          coll = stream.collect(Collectors.toList());
        }

        Assert.assertEquals(vertices.stream().count(), 0);
        Assert.assertEquals(coll.size(), 0);

        Iterator iterator = coll.iterator();
        int i = 0;
        while (iterator.hasNext()) {
          iterator.next();
          i++;
        }
        Assert.assertEquals(0, i);
        Assert.assertEquals(0, index.getInternal().size());

        db.rollback();

        query = "select from C1 where p1 lucene \"abc\" ";
        vertices = db.query(query);

        Assert.assertEquals(1, vertices.stream().count());

        Assert.assertEquals(1, index.getInternal().size());
      }
      orientdb.drop("updateTxTest");
    }
  }

  @Test
  public void txUpdateTest() {

    try (OrientDB orientdb = new OrientDB("memory", OrientDBConfig.defaultConfig())) {
      orientdb.execute(
          "create database updateTxTest memory users(admin identified by 'adminpwd' role"
              + " admin)");

      try (ODatabaseDocumentInternal db =
          (ODatabaseDocumentInternal) orientdb.open("updateTxTest", "admin", "adminpwd")) {
        createSchema(db);

        OIndex index = db.getMetadata().getIndexManagerInternal().getIndex(db, "C1.p1");

        Assert.assertEquals(0, index.getInternal().size());

        db.begin();

        ODocument doc = new ODocument("c1");
        doc.field("p1", new String[] {"update removed", "update fixed"});

        db.save(doc);

        String query = "select from C1 where p1 lucene \"update\" ";
        OResultSet vertices = db.query(query);

        Assert.assertEquals(vertices.stream().count(), 1);

        Assert.assertEquals(2, index.getInternal().size());

        db.commit();

        query = "select from C1 where p1 lucene \"update\" ";
        //noinspection deprecation
        vertices = db.query(query);

        Collection coll;
        try (final Stream<ORID> stream = index.getInternal().getRids("update")) {
          coll = stream.collect(Collectors.toList());
        }

        OResult resultRecord = vertices.next();
        Assert.assertEquals(2, coll.size());
        Assert.assertEquals(2, index.getInternal().size());

        db.begin();

        // select in transaction while updating
        OElement record = resultRecord.getElement().get();
        Collection p1 = record.getProperty("p1");
        p1.remove("update removed");
        db.save(record);

        query = "select from C1 where p1 lucene \"update\" ";
        vertices = db.query(query);
        try (Stream<ORID> stream = index.getInternal().getRids("update")) {
          coll = stream.collect(Collectors.toList());
        }

        Assert.assertEquals(vertices.stream().count(), 1);
        Assert.assertEquals(coll.size(), 1);

        Iterator iterator = coll.iterator();
        int i = 0;
        while (iterator.hasNext()) {
          iterator.next();
          i++;
        }
        Assert.assertEquals(i, 1);

        Assert.assertEquals(1, index.getInternal().size());

        query = "select from C1 where p1 lucene \"update\"";
        vertices = db.query(query);

        try (Stream<ORID> stream = index.getInternal().getRids("update")) {
          coll = stream.collect(Collectors.toList());
        }
        Assert.assertEquals(coll.size(), 1);

        Assert.assertEquals(vertices.stream().count(), 1);

        db.rollback();

        query = "select from C1 where p1 lucene \"update\" ";
        vertices = db.query(query);

        Assert.assertEquals(1, vertices.stream().count());

        Assert.assertEquals(2, index.getInternal().size());
      }
      orientdb.drop("updateTxTest");
    }
  }

  @Test
  public void txUpdateTestComplex() {

    try (OrientDB orientdb = new OrientDB("memory", OrientDBConfig.defaultConfig())) {
      orientdb.execute(
          "create database updateTxTest memory users(admin identified by 'adminpwd' role"
              + " admin)");

      try (ODatabaseDocumentInternal db =
          (ODatabaseDocumentInternal) orientdb.open("updateTxTest", "admin", "adminpwd")) {
        createSchema(db);
        OIndex index = db.getMetadata().getIndexManagerInternal().getIndex(db, "C1.p1");

        Assert.assertEquals(0, index.getInternal().size());

        db.begin();

        ODocument doc = new ODocument("c1");
        doc.field("p1", new String[] {"abc"});

        ODocument doc1 = new ODocument("c1");
        doc1.field("p1", new String[] {"abc"});

        db.save(doc1);
        db.save(doc);

        db.commit();

        db.begin();

        doc.field("p1", new String[] {"removed"});
        db.save(doc);

        String query = "select from C1 where p1 lucene \"abc\"";
        OResultSet vertices = db.query(query);
        Collection coll;
        try (Stream<ORID> stream = index.getInternal().getRids("abc")) {
          coll = stream.collect(Collectors.toList());
        }

        Assert.assertEquals(1, vertices.stream().count());
        Assert.assertEquals(1, coll.size());

        Iterator iterator = coll.iterator();
        int i = 0;
        ORecordId rid = null;
        while (iterator.hasNext()) {
          rid = (ORecordId) iterator.next();
          i++;
        }

        Assert.assertEquals(1, i);
        Assert.assertNotNull(doc1);
        Assert.assertNotNull(rid);
        Assert.assertEquals(doc1.getIdentity().toString(), rid.getIdentity().toString());
        Assert.assertEquals(2, index.getInternal().size());

        query = "select from C1 where p1 lucene \"removed\" ";
        vertices = db.query(query);
        try (Stream<ORID> stream = index.getInternal().getRids("removed")) {
          coll = stream.collect(Collectors.toList());
        }

        Assert.assertEquals(1, vertices.stream().count());
        Assert.assertEquals(1, coll.size());

        db.rollback();

        query = "select from C1 where p1 lucene \"abc\" ";
        vertices = db.query(query);

        Assert.assertEquals(2, vertices.stream().count());

        Assert.assertEquals(2, index.getInternal().size());
      }
      orientdb.drop("updateTxTest");
    }
  }
}
